// -*- c -*-
//
// %CopyrightBegin%
//
// Copyright Ericsson AB 2017-2024. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// %CopyrightEnd%
//

new_map(Dst, Live, N) {
    Eterm res;

    HEAVY_SWAPOUT;
    res = erts_gc_new_map(c_p, reg, $Live, $N, $NEXT_INSTRUCTION);
    HEAVY_SWAPIN;
    $REFRESH_GEN_DEST();
    $Dst = res;
    $NEXT($NEXT_INSTRUCTION+$N);
}

i_new_small_map_lit(Dst, Live, Keys) {
    Eterm res;
    Uint n;
    Eterm keys = $Keys;

    HEAVY_SWAPOUT;
    res = erts_gc_new_small_map_lit(c_p, reg, keys, $Live, $NEXT_INSTRUCTION);
    HEAVY_SWAPIN;
    $REFRESH_GEN_DEST();
    $Dst = res;
    n = arityval(*tuple_val(keys));
    $NEXT($NEXT_INSTRUCTION+n);
}

i_get_map_element(Fail, Src, Key, Dst) {
    Eterm res = get_map_element($Src, $Key);
    if (is_non_value(res)) {
        $FAIL($Fail);
    }
    $Dst = res;
}

i_get_map_element_hash(Fail, Src, Key, Hx, Dst) {
     Eterm res = get_map_element_hash($Src, $Key, $Hx);
     if (is_non_value(res)) {
        $FAIL($Fail);
     }
     $Dst = res;
}

i_get_map_elements(Fail, Src, N) {
    const BeamInstr *fs;
    Eterm map;
    Uint sz, n;

    map = $Src;

    /* This instruction assumes Arg1 is a map,
     * i.e. that it follows a test is_map if needed.
     */

    n  = (Uint)$N / 3;
    fs = $NEXT_INSTRUCTION;

    if (is_flatmap(map)) {
	flatmap_t *mp;
	Eterm *ks;
	Eterm *vs;

	mp = (flatmap_t *)flatmap_val(map);
	sz = flatmap_get_size(mp);

	if (sz == 0) {
            $FAIL($Fail);
	}

	ks = flatmap_get_keys(mp);
	vs = flatmap_get_values(mp);

	while(sz) {
	    if (EQ((Eterm) fs[0], *ks)) {
		PUT_TERM_REG(*vs, fs[1]);
		n--;
		fs += 3;
		/* no more values to fetch, we are done */
		if (n == 0) {
                    $NEXT(fs);
		}
	    }
	    ks++, sz--, vs++;
	}
        $FAIL($Fail);
    } else {
	const Eterm *v;
	erts_ihash_t hx;
	ASSERT(is_hashmap(map));
	while(n--) {
	    hx = fs[2];
	    ASSERT(hx == hashmap_make_hash((Eterm)fs[0]));
	    if ((v = erts_hashmap_get(hx, (Eterm)fs[0], map)) == NULL) {
		$FAIL($Fail);
	    }
	    PUT_TERM_REG(*v, fs[1]);
	    fs += 3;
	}
        $NEXT(fs);
    }
}

update_map_assoc := update_map_assoc.fetch.execute;

update_map_assoc.head() {
    Eterm map;
}

update_map_assoc.fetch(Src) {
    map = $Src;
}

update_map_assoc.execute(Dst, Live, N) {
    Eterm res;
    Uint live = $Live;

    reg[live] = map;
    HEAVY_SWAPOUT;
    res = erts_gc_update_map_assoc(c_p, reg, live, $N, $NEXT_INSTRUCTION);
    HEAVY_SWAPIN;
    ASSERT(is_value(res));
    $REFRESH_GEN_DEST();
    $Dst = res;
    $NEXT($NEXT_INSTRUCTION+$N);
}

update_map_exact := update_map_exact.fetch.execute;

update_map_exact.head() {
    Eterm map;
}

update_map_exact.fetch(Src) {
    map = $Src;
}

update_map_exact.execute(Fail, Dst, Live, N) {
    Eterm res;
    Uint live = $Live;

    reg[live] = map;
    HEAVY_SWAPOUT;
    res = erts_gc_update_map_exact(c_p, reg, live, $N, $NEXT_INSTRUCTION);
    HEAVY_SWAPIN;
    if (is_value(res)) {
        $REFRESH_GEN_DEST();
        $Dst = res;
        $NEXT($NEXT_INSTRUCTION+$N);
    } else {
        $FAIL_HEAD_OR_BODY($Fail);
    }
}
